import json
import threading
import time
from datetime import datetime

import numpy as np
import requests
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding

with open("public_key.pem", "rb") as key_file:
    public_key = serialization.load_pem_public_key(key_file.read())

try:
    with open("config.json", "r", encoding='utf-8') as f:
        config = json.load(f)
except:
    with open("../temp/config_0.json", "r", encoding='utf-8') as f:
        config = json.load(f)
        config["SERVER_IP"] = "127.0.0.1"
# 定义收缩压和舒张压的均值和标准差
sbps = np.array(
    [131, 118, 114, 114, 118, 122, 144, 140, 140, 133, 130, 122, 123, 120, 117, 118, 119, 122, 122, 125, 119, 131, 144,
     137])
dbps = np.array([70, 68, 68, 69, 77, 86, 90, 88, 82, 81, 79, 81, 76, 74, 77, 73, 73, 77, 81, 85, 87, 89, 83, 80])
pps = sbps - dbps

# 读取配置
# 线程数量
NUM_THREADS = config["NUM_THREADS"]
# 设备数量
URL = "http://" + config["SERVER_IP"] + ":" + str(config["SERVER_PORT"]) + "/"
# 模拟时间（小时）
SIMULATE_TIME = config["SIMULATE_TIME"]
# 模拟时间加速倍数
TIME_SPEEDUP = config["TIME_SPEEDUP"]
# 当前时间
start_time_global = time.time()
# 请求超时时间
TIMEOUT = 20


def encrypt_data(data):
    """
    使用 RSA 公钥加密数据。
    :param data: 要加密的字符串数据
    :return: 加密后的数据（字节格式）
    """
    return public_key.encrypt(
        data.encode(),
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )


class Device:

    def __init__(self, device_id, tickets):
        self.access_token = None  # 访问令牌
        self.device_id = device_id  # 设备 ID
        self.ticket = tickets  # 设备票据
        self.get_token()

    def generate_data(self, time_simulate: float):
        """
        生成模拟数据并发送到服务器。
        :param time_simulate: 模拟时间戳
        """
        time_simulate = datetime.fromtimestamp(time_simulate)
        # 获得当前时间小时数
        now_hour = time_simulate.hour
        now_minute = time_simulate.minute
        # 生成模拟数据
        dbp = np.random.normal(dbps[now_hour], 12)
        pp = np.random.normal(pps[now_hour], 9)
        sbp = dbp + pp
        time_float = time_simulate.timestamp()
        data = {"id": self.device_id, "time": time_float, "h": now_hour, "m": now_minute, "sbp": sbp, "dbp": dbp}
        json_data = json.dumps(data)  # 将字典转换为 JSON 格式字符串
        encrypted_data = encrypt_data(json_data)  # 使用公钥加密 JSON 字符串
        encrypted_data_hex = encrypted_data.hex()  # 将加密后的数据转换为十六进制格式（以便通过 JSON 传输）
        self.push_data(encrypted_data_hex)
        # print(json_data)

    def push_data(self, data, retries=5, timeout=TIMEOUT):
        """
        将加密数据发送到服务器。
        :param data: 加密数据（十六进制格式）
        :param retries: 重试次数
        :param timeout: 请求超时时间
        """
        error = []
        for attempt in range(retries):
            try:
                headers = {"Authorization": f"Bearer {self.access_token}"}
                response = requests.post(URL + "data", json={"data": data}, timeout=timeout, headers=headers)
                if response.status_code == 401 and "error" in response.json():
                    self.get_token()
                    continue
                response.raise_for_status()  # 检查请求是否成功
                return
            except Exception as e:
                error.append(str(e))
        print(f"Error: " + str(error))

    def get_token(self, retries=5, timeout=TIMEOUT):
        """
        获取访问令牌。
        :param retries: 重试次数
        :param timeout: 请求超时时间
        """
        error = []
        for attempt in range(retries):
            try:
                response = requests.post(URL + "get_access_token", json={"ticket": self.ticket}, timeout=timeout)
                response.raise_for_status()  # 检查请求是否成功
                self.access_token = response.json().get("access_token")
                return
            except Exception as e:
                error.append(str(e))
        print(f"Error: " + str(error))


def start_devices(device_id, tickets):
    """
    启动设备。
    :param device_id: 设备 ID
    :param tickets: 设备票据
    """
    device = Device(device_id, tickets)
    # 每10分钟一条数据，模拟次数
    simulate_times = int(SIMULATE_TIME * 6)
    for i in range(simulate_times):
        # 模拟时间
        time_simulate = start_time_global + i * 600
        # 实际时间
        start = start_time_global + i * 600 / TIME_SPEEDUP
        try:
            device.generate_data(time_simulate)
        except Exception as e:
            print(f"Error: {e}")
        sleep_time = max(600 / TIME_SPEEDUP - time.time() + start, 0.1)
        time.sleep(sleep_time)


if __name__ == '__main__':
    device_config = config["devices"]
    threads = []  # 线程列表
    for i in range(min(NUM_THREADS, len(device_config))):
        device_id_i = device_config[i]["device_id"]
        tickets_i = device_config[i]["ticket"]
        # 创建线程
        thread = threading.Thread(target=start_devices, args=(device_id_i, tickets_i,))
        # 将线程加入线程列表
        threads.append(thread)
        # 启动线程
        thread.start()
        time.sleep(1)
    for thread in threads:
        # 等待所有线程结束
        thread.join()
